#pragma once

#include <atomic>
#include <mutex>
#include <vector>
#include <chrono>
#include <algorithm>
#include <iostream>
#include <iomanip>

namespace kwdb {

class Metrics {
public:
    Metrics() {
        latencies_.reserve(10000);
    }

    // Record successful write
    void RecordSuccess(std::chrono::microseconds latency) {
        writes_ok_.fetch_add(1);
        RecordLatency(latency.count());
    }

    // Record failed write
    void RecordFailure() {
        writes_failed_.fetch_add(1);
    }

    // Get metrics
    size_t GetSuccessCount() const { return writes_ok_.load(); }
    size_t GetFailureCount() const { return writes_failed_.load(); }
    size_t GetTotalCount() const { return writes_ok_.load() + writes_failed_.load(); }
    double GetSuccessRate() const {
        size_t total = GetTotalCount();
        return total > 0 ? (double)writes_ok_.load() / total * 100.0 : 0.0;
    }

    // Calculate percentiles
    struct LatencyStats {
        double p50;
        double p95;
        double p99;
        double mean;
        size_t count;
    };

    LatencyStats GetLatencyStats() {
        std::lock_guard<std::mutex> lock(latency_mutex_);

        LatencyStats stats{};
        stats.count = latencies_.size();

        if (latencies_.empty()) {
            return stats;
        }

        // Sort for percentile calculation
        std::vector<int64_t> sorted = latencies_;
        std::sort(sorted.begin(), sorted.end());

        // Calculate percentiles
        size_t p50_idx = sorted.size() * 50 / 100;
        size_t p95_idx = sorted.size() * 95 / 100;
        size_t p99_idx = sorted.size() * 99 / 100;

        stats.p50 = sorted[p50_idx] / 1000.0;  // Convert to ms
        stats.p95 = sorted[p95_idx] / 1000.0;
        stats.p99 = sorted[p99_idx] / 1000.0;

        // Calculate mean
        double sum = 0;
        for (auto val : sorted) {
            sum += val;
        }
        stats.mean = (sum / sorted.size()) / 1000.0;

        return stats;
    }

    // Print metrics report
    void PrintReport() {
        auto stats = GetLatencyStats();

        std::cout << "\n========================================" << std::endl;
        std::cout << "           Metrics Report" << std::endl;
        std::cout << "========================================" << std::endl;
        std::cout << "Writes OK:      " << writes_ok_ << std::endl;
        std::cout << "Writes Failed:  " << writes_failed_ << std::endl;
        std::cout << "Success Rate:   " << std::fixed << std::setprecision(2)
                  << GetSuccessRate() << "%" << std::endl;

        if (stats.count > 0) {
            std::cout << "\nLatency Statistics (ms):" << std::endl;
            std::cout << "  Mean:   " << std::fixed << std::setprecision(3)
                      << stats.mean << std::endl;
            std::cout << "  P50:    " << std::fixed << std::setprecision(3)
                      << stats.p50 << std::endl;
            std::cout << "  P95:    " << std::fixed << std::setprecision(3)
                      << stats.p95 << std::endl;
            std::cout << "  P99:    " << std::fixed << std::setprecision(3)
                      << stats.p99 << std::endl;
        }
        std::cout << "========================================" << std::endl;
    }

    // Reset all metrics
    void Reset() {
        writes_ok_ = 0;
        writes_failed_ = 0;

        std::lock_guard<std::mutex> lock(latency_mutex_);
        latencies_.clear();
    }

private:
    void RecordLatency(int64_t microseconds) {
        std::lock_guard<std::mutex> lock(latency_mutex_);

        // Keep only last N samples to avoid unbounded memory growth
        if (latencies_.size() >= 100000) {
            // Simple rotation: remove first half when full
            latencies_.erase(latencies_.begin(),
                           latencies_.begin() + latencies_.size() / 2);
        }

        latencies_.push_back(microseconds);
    }

    std::atomic<size_t> writes_ok_{0};
    std::atomic<size_t> writes_failed_{0};

    std::vector<int64_t> latencies_;
    mutable std::mutex latency_mutex_;
};

// Global metrics instance (singleton pattern)
class GlobalMetrics {
public:
    static Metrics& Instance() {
        static Metrics instance;
        return instance;
    }

private:
    GlobalMetrics() = default;
};

} // namespace kwdb